﻿using System;
using System.Collections.Generic;
using System.Linq;
using msilgc.cbuilder.CSyntaxTree;
using msilgc.cbuilder.MSILSyntaxTree;
using msilgc.CSyntaxTree;
using System.Reflection.Emit;

namespace msilgc.cbuilder.CBuilder
{
    internal class LoadConstantTranslator : ITranslator {
        private readonly VariableFactory _varFactory;
        private readonly IDictionary<OpCode, int> _constTable;

        public LoadConstantTranslator(VariableFactory varFactory) {
            _varFactory = varFactory;
            _constTable = new Dictionary<OpCode, int> {
                                                          {OpCodes.Ldc_I4_M1, (-1)},
                                                          {OpCodes.Ldc_I4_0, 0},
                                                          {OpCodes.Ldc_I4_1, 1},
                                                          {OpCodes.Ldc_I4_2, 2},
                                                          {OpCodes.Ldc_I4_3, 3},
                                                          {OpCodes.Ldc_I4_4, 4},
                                                          {OpCodes.Ldc_I4_5, 5},
                                                          {OpCodes.Ldc_I4_6, 6},
                                                          {OpCodes.Ldc_I4_7, 7},
                                                          {OpCodes.Ldc_I4_8, 8}
                                                      };
        }

        public ICStatement TranslateAndAdd(CMethodBody methodBody, CBasicBlock currentBlock, BasicBlock block, Instruction instruction) {
            if (_constTable.ContainsKey(instruction.Inst)) {
                int constant = _constTable[instruction.Inst];
                ICStatement stm = new AssignConstant(_varFactory.CreateNext(typeof(int)), constant);
                currentBlock.Statements.Add(stm);
                return stm;
            } else {
                object constant;
                Type constType;
                if(instruction.Inst == OpCodes.Ldc_I4) {
                    constant = BitConverter.ToInt32(instruction.Data, 0);
                    constType = typeof(int);
                } else if(instruction.Inst == OpCodes.Ldc_R4) {
                    constant = BitConverter.ToSingle(instruction.Data, 0);
                    constType = typeof(float);
                } else if(instruction.Inst == OpCodes.Ldc_R8) {
                    constant = BitConverter.ToDouble(instruction.Data, 0);
                    constType = typeof(double);
                } else if(instruction.Inst == OpCodes.Ldc_I4_S) {
                    constant = (int)instruction.Data.ElementAt(0);
                    constType = typeof(int);
                } else if(instruction.Inst == OpCodes.Ldc_I8) {
                    constant = BitConverter.ToInt64(instruction.Data, 0);
                    constType = typeof(long);
                } else if(instruction.Inst == OpCodes.Ldstr) {
                    //constant = Convert.ToString(instruction.Data);
                    constant = instruction.InlineString;
                    constType = typeof (string);
                } else {
                    throw new Exception("unknown instruction: " + instruction);
                }
                ICStatement stm = new AssignConstant(_varFactory.CreateNext(constType), constant);
                currentBlock.Statements.Add(stm);
                return stm;
            }
        }

       /* private static float ToFloat(byte[] data) {
            return (float)((Data.ElementAt(0) << 0x0) | (data[1] << 0x8) | (data[2] << 0x10) | (data[3] << 0x18));
        }

        private static double ToDouble(byte[] data) {
            return (double)((Data.ElementAt(0) << 0x0) | (data[1] << 0x8) | (data[2] << 0x10) |
                          (data[3] << 0x18) | (data[4] << 0x20) | (data[5] << 0x28) |
                          (data[6] << 0x30) | (data[7] << 0x38));
        }

        private static long ToInt64(byte[] data) {
            return (long)((Data.ElementAt(0) << 0x0) | (data[1] << 0x8) | (data[2] << 0x10) |
                          (data[3] << 0x18) | (data[4] << 0x20) | (data[5] << 0x28) |
                          (data[6] << 0x30) | (data[7] << 0x38));
        }

        private static int ToInt32(byte[] data) {
            return (Data.ElementAt(0) << 0x0) | (data[1] << 0x8) | (data[2] << 0x10) | (data[3] << 0x18);
        }

        private static int ToInt16(byte[] data) {
            return (Data.ElementAt(0) << 0x0) | (data[1] << 0x8);
        }

        private static int ToInt8(byte[] data) {
            return (Data.ElementAt(0) << 0x0);
        }*/
    }
}